/*
 * $QNXtpLicenseC:
 * Copyright 2007, QNX Software Systems. All Rights Reserved.
 * 
 * You must obtain a written license from and pay applicable license fees to QNX 
 * Software Systems before you may reproduce, modify or distribute this software, 
 * or any work that includes all or part of this software.   Free development 
 * licenses are available for evaluation and non-commercial purposes.  For more 
 * information visit http://licensing.qnx.com or email licensing@qnx.com.
 *  
 * This file may contain contributions from others.  Please review this entire 
 * file for other proprietary rights or license notices, as well as the QNX 
 * Development Suite License Guide at http://licensing.qnx.com/license-guide/ 
 * for other information.
 * $
 */





/* $Source$
 *
 * $Revision: 209233 $
 *
 * DESCRIPTION
 *
 *	Pax is the archiver described in IEEE P1003.2.  It is an archiver
 *	which understands both tar and cpio archives and has a new interface.
 *
 * SYNOPSIS
 *
 *	pax -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]
 *	pax -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]
 *	pax -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]...]
 *	       [-t device][-x format][pathname...]
 *	pax -r -w [-ilmopuvy][-s replstr][pathname...] directory
 *
 * DESCRIPTION
 *
 * 	PAX - POSIX conforming tar and cpio archive handler.  This
 *	program implements POSIX conformant versions of tar, cpio and pax
 *	archive handlers for UNIX.  These handlers have defined befined
 *	by the IEEE P1003.2 commitee.
 *
 * COMPILATION
 *
 *	A number of different compile time configuration options are
 *	available, please see the Makefile and config.h for more details.
 *
 * AUTHOR
 *
 *     Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
 *
 *
 * Sponsored by The USENIX Association for public distribution. 
 *
 * Copyright (c) 1989 Mark H. Colburn.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice is duplicated in all such 
 * forms and that any documentation, advertising materials, and other 
 * materials related to such distribution and use acknowledge that the 
 * software was developed * by Mark H. Colburn and sponsored by The 
 * USENIX Association. 
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * $Log$
 * Revision 1.8  2007/04/18 16:39:10  rmansfield
 * PR: 46583
 * CI: jbaker
 *
 * pax should list to stdout not stderr. This reverts the change by kewarken
 * in PR 4648.
 *
 * Revision 1.7  2005/06/03 01:37:53  adanko
 *
 * Replace existing QNX copyright licence headers with macros as specified by
 * the QNX Coding Standard. This is a change to source files in the head branch
 * only.
 *
 * Note: only comments were changed.
 *
 * PR25328
 *
 * Revision 1.6  2004/03/02 16:15:37  kewarken
 * Fix for PR:4648.  Make verbose output go to stderr.
 *
 * Revision 1.5  2003/08/27 18:16:57  martin
 * Add QSSL Copyright to cover QNX contributions.
 *
 * Revision 1.4  1997/07/29 20:51:05  arthurc
 * *** empty log message ***
 *
 * Revision 1.3  1997/07/10 18:55:28  arthur
 * fix the command line option for pax, -a should use in conjunction with -w
 * only, cannot work bi itself
 *
 * Revision 1.2  1995/01/03 19:11:35  garry
 * Added -Wa option to reset access times.
 *
 * Revision 1.1  1994/12/22  16:06:35  garry
 * Initial revision
 *
 * Revision 1.1  1994/12/22  15:49:30  garry
 * Initial revision
 *
 * Revision 1.2  89/02/12  10:05:17  mark
 * 1.2 release fixes
 * 
 * Revision 1.1  88/12/23  18:02:23  mark
 * Initial revision
 * 
 */

#ifndef lint
static char *ident = "$Id: pax.c 209233 2008-12-15 14:28:06Z CHarris@qnx.com $";
static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
#endif /* ! lint */


/* Headers */

#define NO_EXTERN
#include "pax.h"


/* Globally Available Identifiers */

char           *ar_file;		/* File containing name of archive */
char           *bufend;			/* End of data within archive buffer */
char           *bufstart;		/* Archive buffer */
char           *bufidx;			/* Archive buffer index */
char           *myname;			/* name of executable (argv[0]) */
char          **n_argv;			/* Argv used by name routines */
int             n_argc;			/* Argc used by name routines */
int             archivefd;		/* Archive file descriptor */
int             blocking;		/* Size of each block, in records */
int             gid;			/* Group ID */
int             head_standard;		/* true if archive is POSIX format */
int             ar_interface;		/* defines interface we are using */
int             ar_format;		/* defines current archve format */
int             mask;			/* File creation mask */
int             ttyf;			/* For interactive queries */
int             uid;			/* User ID */
int		names_from_stdin;	/* names for files are from stdin */
int             paxerr;			/* Posix compliancy on error messages */
OFFSET          total;			/* Total number of bytes transferred */
short           f_access_time;		/* Reset access times of input files */
short           f_restore_access;	/* Restore access times of files read */
short           areof;			/* End of input volume reached */
short           f_dir_create;		/* Create missing directories */
short           f_append;		/* Add named files to end of archive */
short           f_create;		/* create a new archive */
short           f_extract;		/* Extract named files from archive */
short           f_follow_links;		/* follow symbolic links */
short           f_interactive;		/* Interactivly extract files */
short           f_linksleft;		/* Report on unresolved links */
short           f_list;			/* List files on the archive */
short           f_modified;		/* Don't restore modification times */
short           f_verbose;		/* Turn on verbose mode */
short		f_link;			/* link files where possible */
short		f_owner;		/* extract files as the user */
short		f_pass;			/* pass files between directories */
short           f_newer;		/* append files to archive if newer */
short		f_disposition;		/* ask for file disposition */
short           f_reverse_match;	/* Reverse sense of pattern match */
short           f_mtime;		/* Retain file modification time */
short           f_unconditional;	/* Copy unconditionally */
time_t          now = 0;		/* Current time */
uint            arvolume;		/* Volume number */
uint            blocksize = BLOCKSIZE;	/* Archive block size */
FILE	       *msgfile;		/* message outpu file stdout/stderr */
Replstr        *rplhead = (Replstr *)NULL;	/*  head of replstr list */
Replstr        *rpltail;		/* pointer to tail of replstr list */


/* Function Prototypes */

#ifdef __STDC__

static void 	usage(void);
static OFFSET   pax_optsize(char *);

#else /* !__STDC__ */

static void 	usage();
static OFFSET   pax_optsize();

#endif /* __STDC__ */


/* main - main routine for handling all archive formats.
 *
 * DESCRIPTION
 *
 * 	Set up globals and call the proper interface as specified by the user.
 *
 * PARAMETERS
 *
 *	int argc	- count of user supplied arguments
 *	char **argv	- user supplied arguments 
 *
 * RETURNS
 *
 *	Returns an exit code of 0 to the parent process.
 */

#ifdef __STDC__

int main(int argc, char **argv)

#else

int main(argc, argv)
int             argc;
char          **argv;

#endif
{
    /* strip the pathname off of the name of the executable */
    myname = basename( argv[0] );
	paxerr = 0;

    /* set upt for collecting other command line arguments */
    name_init(argc, argv);

    /* get all our necessary information */
    mask = umask(0);
    uid = getuid();
    gid = getgid();
    now = time((time_t *) 0);

    /* open terminal for interactive queries */
    ttyf = open_tty();

    if (IS_EXE_NAME(myname, "tar")) {
	do_tar(argc, argv);
    } else if (IS_EXE_NAME(myname, "cpio")) {
	do_cpio(argc, argv);
    } else {
	do_pax(argc, argv);
    }
    return(paxerr);
    /* NOTREACHED */
}


/* do_pax - provide a PAX conformant user interface for archive handling
 *
 * DESCRIPTION
 *
 *	Process the command line parameters given, doing some minimal sanity
 *	checking, and then launch the specified archiving functions.
 *
 * PARAMETERS
 *
 *    int ac		- A count of arguments in av.  Should be passed argc 
 *			  from main
 *    char **av		- A pointer to an argument list.  Should be passed 
 *			  argv from main
 *
 * RETURNS
 *
 *    Normally returns 0.  If an error occurs, -1 is returned 
 *    and state is set to reflect the error.
 *
 */

#ifdef __STDC__

int do_pax(int ac, char **av)

#else

int do_pax(ac, av)
int             ac;		/* argument counter */
char          **av;		/* arguments */

#endif
{
    int             c;
    char	   *dirname;
    Stat	    st;

    /* default input/output file for PAX is STDIN/STDOUT */
    ar_file = "-";

    /*
     * set up the flags to reflect the default pax inteface.  Unfortunately
     * the pax interface has several options which are completely opposite
     * of the tar and/or cpio interfaces...
     */
    f_unconditional = 1;
    f_mtime = 1;
    f_dir_create = 1;
    f_list = 1;
    blocksize = 0;
    blocking = 0;
    ar_interface = PAX;
    ar_format = TAR;	/* default interface if none given for -w */
    msgfile=stdout;

    while ((c = getopt(ac, av, "ab:cdf:ilmoprs:t:uvW:wx:y")) != EOF) {
	switch (c) {
	case 'a':
	    f_append = 1;
	    f_list = 0;
	    break;
	case 'b':
	    if ((blocksize = pax_optsize(optarg)) == 0) {
		fatal("Bad block size");
	    }
	    break;
	case 'c':
	    f_reverse_match = 1;
	    break;
	case 'd':
	    f_dir_create = 0;
	    break;
	case 'f':
	    if (blocksize == 0) {
		blocking = 1;
		blocksize = 1 * BLOCKSIZE;
	    }
	    ar_file = optarg;
	    break;
	case 'i':
	    f_interactive = 1;
	    break;
	case 'l':
	    f_link = 1;
	    break;
	case 'm':
	    f_mtime = 0;
	    break;
	case 'o':
	    f_owner = 1;
	    break;
	case 'p':
	    f_access_time = 1;
	    break;
	case 'r':
	    if (f_create) {
		f_create = 0;
		f_pass = 1;
	    } else {
		f_list = 0;
		f_extract = 1;
	    } 
	    msgfile=stderr;
	    break;
	case 's':
	    add_replstr(optarg);
	    break;
	case 't':
	    if (blocksize == 0) {
		blocking = 1;
		blocksize = 10 * BLOCKSIZE;
	    }
	    ar_file = optarg;
	    break;
	case 'u':
	    f_unconditional = 0;
	    break;
	case 'v':
	    f_verbose = 1;
	    break;
	case 'W':
	    if(*optarg == 'a')
	        f_restore_access = 1;
	    break;
	case 'w':
	    if (f_extract) {
		f_extract = 0;
		f_pass = 1;
	    } else {
		f_list = 0;
		f_create = 1;
	    } 
	    msgfile=stderr;
	    break;
	case 'x':
	    if (strcmp(optarg, "ustar") == 0) {
		ar_format = TAR;
	    } else if (strcmp(optarg, "cpio") == 0) {
		ar_format = CPIO;
	    } else {
		usage();
	    }
	    break;
	case 'y':
	    f_disposition = 1;
	    break;
	default:
	    usage();
	}
    }

    if (blocksize == 0) {
	blocking = 1;
	blocksize = blocking * BLOCKSIZE;
    }
    buf_allocate((OFFSET) blocksize);

    if (f_extract || f_list) {
		open_archive(AR_READ);
		get_archive_type();
		read_archive();
    } 
	else if (f_create) {
		if( f_append && ( access( ar_file, F_OK ) == 0 ) ) {
			open_archive( AR_APPEND );
			get_archive_type();
			append_archive();
		} else {
		
			if (optind >= n_argc) {
		    	names_from_stdin++;		/* args from stdin */
			}
			open_archive(AR_WRITE);
			create_archive();
		}
    } 
/*
	else if (f_append) {
		open_archive(AR_APPEND);
		get_archive_type();
		append_archive();
    } 
*/
	else if (f_pass && optind < n_argc) {
		dirname = n_argv[--n_argc];
		if (LSTAT(dirname, &st) < 0) {
		    fatal(strerror());
		}
		if ((st.sb_mode & S_IFMT) != S_IFDIR) {
		    fatal("Not a directory");
		}
		if (optind >= n_argc) {
	    	names_from_stdin++;		/* args from stdin */
		}
		pass(dirname);
    } 
	else {
		usage();
    }

    return (0);
}


/* get_archive_type - determine input archive type from archive header
 *
 * DESCRIPTION
 *
 * 	reads the first block of the archive and determines the archive 
 *	type from the data.  If the archive type cannot be determined, 
 *	processing stops, and a 1 is returned to the caller.  If verbose
 *	mode is on, then the archive type will be printed on the standard
 *	error device as it is determined.
 *
 * FIXME 
 *
 *	be able to understand TAR and CPIO magic numbers
 */

#ifdef __STDC__

void get_archive_type(void)

#else

void get_archive_type()

#endif
{
    if (ar_read() != 0) {
	fatal("Unable to determine archive type.");
    }
    if (strncmp(bufstart, "070707", 6) == 0) {
	ar_format = CPIO;
	if (f_verbose) {
	    fputs("CPIO format archive\n", stderr);
	}
    } else if (strncmp(&bufstart[257], "ustar", 5) == 0) {
	ar_format = TAR;
	if (f_verbose) {
	    fputs("USTAR format archive\n", stderr);
	}
    } else {
	ar_format = TAR;
    }
}


/* pax_optsize - interpret a size argument
 *
 * DESCRIPTION
 *
 * 	Recognizes suffixes for blocks (512-bytes), k-bytes and megabytes.  
 * 	Also handles simple expressions containing '+' for addition.
 *
 * PARAMETERS
 *
 *    char 	*str	- A pointer to the string to interpret
 *
 * RETURNS
 *
 *    Normally returns the value represented by the expression in the 
 *    the string.
 *
 * ERRORS
 *
 *	If the string cannot be interpretted, the program will fail, since
 *	the buffering will be incorrect.
 *
 */

#ifdef __STDC__

static OFFSET pax_optsize(char *str)

#else

static OFFSET pax_optsize(str)
char           *str;		/* pointer to string to interpret */

#endif
{
    char           *idx;
    OFFSET          number;	/* temporary storage for current number */
    OFFSET          result;	/* cumulative total to be returned to caller */

    result = 0;
    idx = str;
    for (;;) {
	number = 0;
	while (*idx >= '0' && *idx <= '9')
	    number = number * 10 + *idx++ - '0';
	switch (*idx++) {
	case 'b':
	    result += number * 512L;
	    continue;
	case 'k':
	    result += number * 1024L;
	    continue;
	case 'm':
	    result += number * 1024L * 1024L;
	    continue;
	case '+':
	    result += number;
	    continue;
	case '\0':
	    result += number;
	    break;
	default:
	    break;
	}
	break;
    }
    if (*--idx) {
	fatal("Unrecognizable value");
    }
    return (result);
}


/* usage - print a helpful message and exit
 *
 * DESCRIPTION
 *
 *	Usage prints out the usage message for the PAX interface and then
 *	exits with a non-zero termination status.  This is used when a user
 *	has provided non-existant or incompatible command line arguments.
 *
 * RETURNS
 *
 *	Returns an exit status of 1 to the parent process.
 *
 */

#ifdef __STDC__

static void usage(void)

#else

static void usage()

#endif
{
    fprintf(stderr, "Usage: %s -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n",
	myname);
    fprintf(stderr, "       %s -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n",
	myname);
    fprintf(stderr, "       %s -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]\n              [-t device] [-x format] [pathname...]\n",
	myname);
    fprintf(stderr, "       %s -r -w [-ilmopuvy] [-s replstr] [pathname...] directory\n",
	myname);
    exit(1);
}
